#from framework.capture__ import *
import os  #会不会出现重复引用问题？（capture.py中也引用了）
from collections import Counter
import operator
from data_type import *
import pickle
import time as time_mine
from datetime import datetime
def net_scan_detection(dpkt:dict):
    
    
    ###########################从pkl文件中获取flowForScanD列表###########################
    flowForScanD = []
    Ethernet = EthernetPkt()
    Ipv4 = Ipv4Pkt()
    Ipv6 = Ipv6Pkt()
    Tcp = TcpPkt()
    Udp = UdpPkt()
    Arp = ArpPkt()
    Dns = DnsPkt()
    Icmpv4 = Icmpv4Pkt()
    Icmpv6 = Icmpv6Pkt()
    HttpReq = HttpReqPkt()
    HttpRes = HttpResPkt()
    
    eachPkt = Packet(Ethernet, Ipv4, Ipv6, Tcp, Udp, Arp, Dns, Icmpv4, Icmpv6, HttpReq, HttpRes)
        
    #with open("C://Users//leslie//Desktop//res//cur//demo.pkl",'rb') as f:
    with open("sniffRes//cur//demo.pkl",'rb') as f:
        while 1:
            try:
                eachPkt = pickle.load(f)
                flowForScanD.append(eachPkt)
                # print(eachPkt)
                # eachPkt.PrintFlag()
            except EOFError:
                break
    ###########################从pkl文件中获取flowForScanD列表###########################
    
    port = ''
    port_list = []
    max_port = 0
    temp_dict = {}
    hacker_dict = {}
    hacker_time = ''
    for i in dpkt:
        if type(i)==Ipv4Pkt or type(i)==Ipv6Pkt:
            ip_time={i.ip():i.time()}
            ip_port={i.ip():i.dport()}
            for ip, port in ip_port.items():
                port_list.append(port)
                z = Counter(port_list)
                max_port = max(z.items(), key=operator.itemgetter(1))[0]
                if ip not in temp_dict:
                    temp_dict.setdefault(port, ip)
            hacker_dict = {v: k for k, v in temp_dict.items() if k == max_port}
            for k, v in ip_time.items():
                for k2 in hacker_dict:
                    if k == k2:
                        hacker_time = ip_time[k]
                
    if hacker_dict!={}:
        data = f' port scanned from {hacker_dict}  at {hacker_time}'
        print(data)

class ip_port_scan:
    def __init__(self):
        self.dip_port=[]#该ip收到消息或曾收到消息的端口号
        self.port_time_and_sip={}#端口号收到的或曾收到的【时间，源ip】
        self.port_count={}#该端口号截止到某个时间点收到了多少报文
        self.all_port_num=0#某目的ip收到的消息端口号总数
        self.sip_port_num={}#源ip访问过的端口
        self.period=10#保留时间周期s
    def add(self,port,time,sip=0):#添加该ip的port(端口)在time(时间)从sip(源ip)收到了某种报文
        if self.dip_port.count(port)==0:
            self.dip_port.append(port)
            self.port_time_and_sip[port]=[[time,sip]]
            self.port_count[port]=1
            self.all_port_num+=1
        else:
            self.port_time_and_sip[port].append([time,sip])
            self.port_count[port]+=1
        if self.sip_port_num.get(sip)==None:
            self.sip_port_num[sip]=[port]
        else:
            self.sip_port_num[sip].append(port)
            
    def sub(self,port=0,sip=0):#减去有关某个端口号或者某个ip的数据
        try:
            if port!=0:
                a=self.port_time_and_sip.pop(port)
                self.port_count.pop(port)
                for i in a:
                    self.sip_port_num[i[1]].remove(port)
                self.dip_port.remove(port)
                self.all_port_num-=1
        except:
            print("the port does not exist")
        try:
            if sip!=0:
                for i in self.sip_port_num[sip]:
                    self.port_count[i]-=1
                    if (self.port_count[i])==0:#该端口号没有报文了
                        self.port_time_and_sip.pop(i)
                        self.dip_port.remove(i)
                        self.port_count.pop(i)
                        self.all_port_num-=1
                    else:
                        for j in self.port_time_and_sip[i]:
                            if sip==j[1]:
                                self.port_time_and_sip[i].remove(j)
                self.sip_port_num.pop(sip)
                        
        except:
            print("the ip does not exist")
            
    def flush(self,time):#去除某个时间之前的消息
        for i in self.dip_port:
            j=0
            while j<len(self.port_time_and_sip[i]):
                time0=self.port_time_and_sip[i][j][0]
                time1=time
                sip=self.port_time_and_sip[i][j][1]
                time_and_sip=[time0,sip]
                
                time0=datetime.strptime(time0, '%Y-%m-%d %H:%M:%S')
                time0 = time_mine.mktime(time0.timetuple())
                time1=datetime.strptime(time1, '%Y-%m-%d %H:%M:%S')
                time1 = time_mine.mktime(time1.timetuple())
                
                if time0+self.period<time1:
                    self.port_time_and_sip[i].remove(time_and_sip)
                    self.port_count[i]-=1
                    j-=1
                    self.sip_port_num[time_and_sip[1]].remove(i)
                    if self.port_count[i]==0:
                        self.dip_port.remove(i)
                        self.port_time_and_sip.pop(i)
                        self.port_count.pop(i)
                        self.all_port_num-=1
                        break
                j+=1
    def printf(self):#输出
        for i in self.dip_port:
            print("port=",i)
            print(self.port_time_and_sip)
            for j in range(len(self.port_time_and_sip[i])):
                print("[time=",self.port_time_and_sip[i][j][0],",source_ip=",self.port_time_and_sip[i][j][1],"]",end=' ')
            print("\nport_count",self.port_count[i])
            #for j in self.sip_port_num:
            print("sip_port_num=",len(self.sip_port_num))
            print(self.sip_port_num)
        print("all_port_num=",self.all_port_num)
        print()
        
    def get_all_port_num(self):
        return self.all_port_num
    
    def get_dip_port(self):
        return self.dip_port
    
    def get_port_count(self,port):
        return self.port_count[port]

    def get_attack_timeandsip(self,port):
        if self.port_time_and_sip.get(port)!=None:
            return self.port_time_and_sip[port]
        else:
            return None

    def get_sip_port_diff_num(self,sip):
        if self.sip_port_num.get(sip)!=None:
            #print(len(set(self.sip_port_num[sip])))
            return len(set(self.sip_port_num[sip]))
        else:
            return 0

#threshold-一个端口在一个周期内最多被访问threshold次
#threshold2-一个ip最多访问threshold2个端口
def port_scan(threshold=8192,threshold2=10,blacklist:list=[]):
    all_ip={}
    all_ip_count=0
    try:
        ###########################从pkl文件中获取flowForScanD列表###########################
        flowForScanD = []
        Ethernet = EthernetPkt()
        Ipv4 = Ipv4Pkt()
        Ipv6 = Ipv6Pkt()
        Tcp = TcpPkt()
        Udp = UdpPkt()
        Arp = ArpPkt()
        Dns = DnsPkt()
        Icmpv4 = Icmpv4Pkt()
        Icmpv6 = Icmpv6Pkt()
        HttpReq = HttpReqPkt()
        HttpRes = HttpResPkt()
        
        eachPkt = Packet(Ethernet, Ipv4, Ipv6, Tcp, Udp, Arp, Dns, Icmpv4, Icmpv6, HttpReq, HttpRes)
            
        #with open("C://Users//leslie//Desktop//res//cur//demo.pkl",'rb') as f:
        with open("sniffRes//cur//demo.pkl",'rb') as f:
            while 1:
                try:
                    eachPkt = pickle.load(f)
                    flowForScanD.append(eachPkt)
                    # print(eachPkt)
                    # eachPkt.PrintFlag()
                except EOFError:
                    break
        ###########################从pkl文件中获取flowForScanD列表###########################
    except:
        print("scan read file error!")
    try:
        count = 0
        return_list = []
        data=''
        for i in flowForScanD:
            # print(123)
            hacker_dict=[]
            if i.IsEthernet()==1:
                time_dpkt=i.GetEthernet()
                if i.IsTcp()==1 or i.IsUdp()==1:
                    if i.IsTcp()==1:
                        port=i.GetTcp()
                    else:
                        port=i.GetUdp()
                    if i.IsIpv4()==1 or i.IsIpv6()==1:
                        if i.IsIpv4()==1:
                            ip=i.GetIpv4()
                        if i.IsIpv6()==1:
                            ip=i.GetIpv6()
                        
                        if blacklist.count(port.GetField("dport"))!=0:
                            hacker_dict.append([time_dpkt.GetField("time"),ip.GetField("srcIp"),port.GetField("dport")])
                            hacker_dict[-1].append("blacklist")
                            continue
                        dip=ip.GetField("dstIp")
                        if all_ip.get(dip)==None:
                            all_ip[dip]=ip_port_scan()
                            all_ip[dip].add(port.GetField("dport"),time_dpkt.GetField("time"),ip.GetField("srcIp"))
                            all_ip_count+=1
                        else:
                            all_ip[dip].add(port.GetField("dport"),time_dpkt.GetField("time"),ip.GetField("srcIp"))
                            all_ip[dip].flush(time_dpkt.GetField("time"))
                            if all_ip[dip].get_port_count(port.GetField("dport"))>=threshold:
                                for i in all_ip[dip].get_attack_timeandsip(port.GetField("dport")):
                                    hacker_dict.append(i)
                                    hacker_dict[-1].append(port.GetField("dport"))
                                    hacker_dict[-1].append("one_port_scan")
                                all_ip[dip].sub(port.GetField("dport"),0)
                            if all_ip[dip].get_sip_port_diff_num(ip.GetField("srcIp"))>=threshold2:
                                hacker_dict.append([time_dpkt.GetField("time"),ip.GetField("srcIp")])
                                hacker_dict[-1].append("ports_scan")
                                all_ip[dip].sub(0,ip.GetField("srcIp"))
            if hacker_dict!=[]:
                for i in hacker_dict:
                    if i[-1]=='blacklist':
                        data += f' blacklist port {i[2]} scanned from {i[1]}  at {i[0]}\n'
                        count += 1
                        return_list.append((count, i[-1], i[2], i[1], i[0]))
                    if i[-1]=='one_port_scan':
                        data += f' port {i[2]} scanned from {i[1]}  at {i[0]}\n'
                        count += 1
                        return_list.append((count, i[-1], i[2], i[1], i[0]))
                    if i[-1]=='ports_scan':
                        data += f' More than {threshold2} ports scanned from {i[1]}  at {i[0]}\n'
                        count += 1
                        return_list.append((count, i[-1], threshold2, i[1], i[0]))
        if data=='':
            return [], "safe"
        else:
            return return_list, data
    except:
        print("error 2")
        return -1, -1
# 类型 行为端口(数) 被扫描端口 时间
